package jude.java.framework.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

import jude.java.money.beans.global.BaseBean;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.type.Type;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

@SuppressWarnings("unchecked")
public abstract class BaseHibernateDao extends HibernateDaoSupport implements Dao {
	
	protected final Log log = LogFactory.getLog(getClass());

	public String defaultOrderBy() {
		return null;
	}

	public <T extends BaseBean> void insert(T o) {
		getHibernateTemplate().save(o);
	}

	public <T extends BaseBean> void update(T o) {
		getHibernateTemplate().update(o);
	}
	
	public <T extends BaseBean> int update(Class<T> clz, String hql, Object[] values) {
		StringBuilder sb = new StringBuilder();
		sb.append("update ").append(clz.getName());
		if (StringUtils.isNotEmpty(hql)) {
			sb.append(" ").append(hql);
		} else {
			values = null;
		}
		return getHibernateTemplate().bulkUpdate(sb.toString(), values);
	}


	public <T extends BaseBean> void insertOrUpdate(T o) {
		getHibernateTemplate().saveOrUpdate(o);
	}
	
	public <T extends BaseBean> void insertOrUpdateAll(Collection<T> o) {
		getHibernateTemplate().saveOrUpdateAll(o);
	}

	public <T extends BaseBean> T get(Class<T> clazz, Serializable id) {
		return (T) getHibernateTemplate().get(clazz, id);
	}
	
	@SuppressWarnings("rawtypes")
	public <T extends BaseBean> T get(Class<T> clazz, String key, Object value) {
		StringBuilder hql = new StringBuilder("from ");
		hql.append(clazz.getName()).append(" as o where o.");
		hql.append(key).append("=?");
		List results = getHibernateTemplate().find(hql.toString(), value);
		if (results == null || results.isEmpty())
			return null;
		T o = (T) results.get(0);
		if (results.size() > 1) {
			System.out.println("more than one result!");
		}
		return o;
	}
	
	public <T extends BaseBean> List<T> listAll(Class<T> clazz) {
//		String orderBy = defaultOrderBy();
//		if (StringUtils.isNotEmpty(orderBy)) {
//			String hql = "from " + clazz.getName() + " as o order by " + orderBy;
//			return (List<T>)find(hql);
//		}
		return getHibernateTemplate().loadAll(clazz);
	}
	
	@Override
	public <T extends BaseBean> List<T> query(Class<T> clazz, String key,
			Object value) {
		if (key == null)
			return listAll(clazz);
		StringBuilder hql = new StringBuilder("from ");
		hql.append(clazz.getName()).append(" as o where o.").append(key).append("=?");
		return getHibernateTemplate().find(hql.toString(), value);
	}
	
	public <T extends BaseBean> List<T> queryWithArray(Class<T> clazz, final int pageSize, final int pageIndex, String query, final Object array) {
		if (query == null)
			return listAll(clazz);
		if (array == null)
			return null;
		final StringBuilder hql = new StringBuilder("from ");
		hql.append(clazz.getName()).append(" as o where ").append(query);
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			@SuppressWarnings("rawtypes")
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query q = session.createQuery(hql.toString());
				if (array.getClass().isArray()) {
					q.setParameterList("array", (Object[])array);
				} else if (array instanceof Collection) {
					q.setParameterList("array", (Collection)array);
				} else{
					return null;
				}
				if (pageSize > 0 && pageIndex >= 0) {
					q.setMaxResults(pageSize);
					q.setFirstResult(pageSize * pageIndex);
				}
				return q.list();
			}
		});
	}

	public <T extends BaseBean> List<T> query(Class<T> clazz, String search, Object... values) {
		if (StringUtils.isEmpty(search))
			return listAll(clazz);
		StringBuilder hql = new StringBuilder("from ");
		hql.append(clazz.getName()).append(" as o where ").append(search);
		return getHibernateTemplate().find(hql.toString(), values);
	}
	
	@Override
	public <T extends BaseBean> List<T> query(final Class<T> clazz, final String search, 
			final int pageSize, final int pageIndex, final Object[] values, final Type[] types) {
		return (List<T>)getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				StringBuilder hql = new StringBuilder();
				hql.append("from ").append(clazz.getName());
				if (search != null) {
					hql.append(" as o");
					if (search.startsWith("order by"))
						hql.append(" ");
					else
						hql.append(" where ");
					hql.append(search);
				}
//				System.out.println(hql);
				Query q = session.createQuery(hql.toString());
				if (search != null && values != null && values.length > 0) {
					if (types == null || types.length < values.length) {
						for (int i = 0; i < values.length; i++) {
							q.setParameter(i, values[i]);	//not null
						}
					} else {
						q.setParameters(values, types);
					}
				}
				if (pageSize > 0 && pageIndex >= 0) {
					q.setFirstResult(pageSize * pageIndex);
					q.setMaxResults(pageSize);
//					q.setFetchSize(pageSize);
				}
				return q.list();
			}
		});
	}

	public <T extends BaseBean> int count(final Class<T> clazz, final String search, final Object[] values, final Type[] types) {
		return (int)(long)(Long)getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				StringBuilder hql = new StringBuilder();
				hql.append("select count(*) from ").append(clazz.getName());
				if (search != null) {
					hql.append(" as o where ").append(search);
				}
				Query q = session.createQuery(hql.toString());
				if (search != null && values != null && values.length > 0) {
					if (types == null || types.length < values.length) {
						for (int i = 0; i < values.length; i++) {
							q.setParameter(i, values[i]);	//not null
						}
					} else {
						q.setParameters(values, types);
					}
				}
				return q.uniqueResult();
			}
		});
	}
	
	public <T extends BaseBean> void delete(T o) {
		getHibernateTemplate().delete(o);
	}

	public <T extends BaseBean> void deleteAll(Collection<T> entities) {
		getHibernateTemplate().deleteAll(entities);
	}

	public <T extends BaseBean> void delete(Class<T> clazz, Serializable id) {
		getHibernateTemplate().delete(get(clazz, id));
	}

	@Override
	public List<?> query(final String hql) {
		return (List<?>)getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query q = session.createQuery(hql);
				return q.list();
			}
		});
	}
	
	public <T extends BaseBean> List<T> queryBySql(final String sql, final Class<T> clz) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query q = session.createSQLQuery(sql).addEntity("o", clz);
				return q.list();
			}
		});
	}
	
	public Object executeSql(final String sql) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query q = session.createSQLQuery(sql);
				return q.list();
			}
		});
	}
	
	public Object executeUniqueResultSql(final String sql) {
		return getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query q = session.createSQLQuery(sql);
				return q.uniqueResult();
			}
		});
	}
	
	public Object get(final String hql) {
		return getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query q = session.createQuery(hql);
				return q.uniqueResult();
			}
		});
	}

	public int update(final String hql) {
		return getHibernateTemplate().bulkUpdate(hql);
//		return (Integer)getHibernateTemplate().execute(new HibernateCallback() {
//			@Override
//			public Object doInHibernate(Session session) throws HibernateException,
//					SQLException {
//				Query q = session.createQuery(hql);
//				return q.executeUpdate();
//			}
//		});
	}

}